//@author A0097758E
/**
 * This class stores a list of indexes that were last touched
 * within a linear Data Structure to facilitate undo/redo.
 */

package storage;

import java.util.LinkedList;

import sharedUtils.Constants;
import sharedUtils.Logger;

public class ActionHistory {
	private static boolean DEBUG = Constants.isDebugModeEnabled;
	
	private LinkedList<Integer> currentStack;
	private LinkedList<Integer> oldStack;
	
	/**
	 * Default constructor. Creates two stacks to track which index of a 
	 * linear Data Structure (outside of this object) that was last 
	 * modified (to facilitate undo/redo). 
	 */
	public ActionHistory() {
		Logger.log("Creating new ActionHistory object", 
				TaskList.class.getSimpleName(), "ActionHistory()");
		
		this.currentStack = new LinkedList<Integer>();
		this.oldStack = new LinkedList<Integer>();
	}
	
	/**
	 * Sets the boolean flag to enable/disable debug code blocks. Default is false.
	 * 
	 * @param d Boolean to set debug mode.
	 * @return Constants.SUCCESS_ERROR_CODE
	 */
	public static int setDebug(boolean d) {
		DEBUG = d;
		return Constants.SUCCESS_ERROR_CODE;
	}
	
	/**
	 * Returns the maximum size that this ActionHistory is set to track.
	 * 
	 * @return Constants.MAX_HISTORY_SIZE
	 */
	public static int getMaxSize() {
		return Constants.MAX_HISTORY_SIZE;
	}
	
	/**
	 * Adds the number corresponding to the index of the most 
	 * recently manipulated element in a linear Data Structure.
	 * 
	 * @param index Index of last modification in linear Data Structure
	 * @return One of the following error codes: Constants.SUCCESS_ERROR_CODE, 
	 * Constants.INVALID_INDEX_ARG, or Constants.ACTIONHISTORY_FAILED_UPDATE_TO_AL
	 */
	public int add(int index) {
		/**
		 * Check argument
		 */
		if(index < 0) {
			return Constants.INVALID_INDEX_ARG;
		}
		
		/**
		 * Logic
		 */
		Logger.log("Adding index: " + index + " as the last index touched.", 
				TaskList.class.getSimpleName(), "add()");
		
		checkForMaxSize();		
		oldStack.clear();
		
		if(!currentStack.add(new Integer(index))) {
			return Constants.ACTIONHISTORY_FAILED_UPDATE_TO_AL;
		}
		
		return Constants.SUCCESS_ERROR_CODE;
	}

	/**
	 * Checks if this history is already at the max allowed size
	 */
	private void checkForMaxSize() {
		if(currentStack.size() >= Constants.MAX_HISTORY_SIZE) {
			Logger.log("Restricting undo history size on ActionHistory", 
					TaskList.class.getSimpleName(), "checkForMaxSize()");
			
			/**
			 * If this addition will make the revisionList too large,
			 * delete one older entry
			 */			
			currentStack.removeFirst();
		}
	}
	
	/**
	 * Backtrack the last index manipulated by one step
	 * 
	 * @return Constants.SUCCESS_ERROR_CODE
	 */
	public int undo() {		
		if (currentStack.size() > 0) {
			Logger.log("Undoing last action on index: " + getIndexOfLastTaskActedOn(), 
					TaskList.class.getSimpleName(), "undo()");
			
			/**
			 * currentStack.pop();
			 * oldStack.push();
			 */
			oldStack.addLast(currentStack.removeLast());
		}

		return Constants.SUCCESS_ERROR_CODE;
	}
	
	/**
	 * Advances the last index manipulated by one step
	 * 
	 * @return Constants.SUCCESS_ERROR_CODE
	 */
	public int redo() {		
		if (oldStack.size() > 0) {
			Logger.log("Redoing last action on index: " + oldStack.peek().intValue(), 
					TaskList.class.getSimpleName(), "redo()");
			
			/**
			 * oldstack.pop();
			 * currentStack.push();
			 */
			currentStack.addLast(oldStack.removeLast());
		}

		return Constants.SUCCESS_ERROR_CODE;
	}
	
	/**
	 * Returns the index of the last element tracked by ActionHistory
	 * 
	 * @return The index of the last element tracked by ActionHistory
	 */
	public int getIndexOfLastTaskActedOn(){
		if(currentStack.size() < 1) {
			return Constants.NO_MODIFICATIONS_WERE_MADE;
		} else {
			return currentStack.getLast().intValue();
		}
	}
	
	/**
	 * Unit Testing Function
	 */
	public int getCurrentStackSize() {
		if(DEBUG) return currentStack.size();
		else return -1;
	}
	
	/**
	 * Unit Testing Function
	 */
	public static boolean getDebugMode() {
		return DEBUG;
	}
}
